﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using Unity.Mathematics;
using UnityEditor;
using UnityEngine;

namespace Mars.GpuInstance
{
    [CustomEditor(typeof(InstancerPrefab))]
    public class InstancerPrefabEditor : Editor
    {
        private InstancerPrefab m_Target;
        private SerializedProperty m_CalculatedLevelsSP;


        private void OnEnable()
        {
            m_Target = (InstancerPrefab)target;
            m_Target.LodDatas.Sort();
        }

        public override void OnInspectorGUI()
        {
            serializedObject.Update();
            EditorGUILayout.PropertyField(serializedObject.FindProperty("m_Script"));
            DrawPropertiesExcluding(serializedObject, new []{"m_Script", "shadowLod"});
            EditorGUILayout.PropertyField(serializedObject.FindProperty("shadowLod"));
            

            if (GUILayout.Button("刷新"))
            {
                m_Target.Refresh();
            }
            
            if (GUILayout.Button("序列化"))
            {
                SerializeTool.SerializeInstanceFile(m_Target);
            }

            serializedObject.ApplyModifiedProperties();

            bool meshRendererMiss = false;
            string lostStr = "";
 
            for (int i = 0; i < m_Target.LodDatas.Count; i++)
            {
                var lodData = m_Target.LodDatas[i];
                
                for (int j = 0; j < lodData.renderDatas.Count; j++)
                {
                    var renderData = lodData.renderDatas[j];
                    if (renderData.meshRenderer == null)
                    {
                        meshRendererMiss = true;
                        lostStr += $"lod:{i}第{j}个渲染数据丢失\n";
                    }
                }
            }

            if (meshRendererMiss)
            {
                EditorGUILayout.HelpBox(lostStr, MessageType.Error);
                return;
            }

            bool hasEmptyRenderData = false;
            lostStr = "";
            for (int i = 0; i < m_Target.LodDatas.Count; i++)
            {
                var lodData = m_Target.LodDatas[i];
                if (lodData.renderDatas.Count <= 0)
                {
                    hasEmptyRenderData = true;
                    lostStr += $"lod:{i} 渲染数据为空\n";
                }
            }

            if (m_Target.LodDatas.Count <= 0)
            {
                hasEmptyRenderData = true;
                lostStr += "没有配置渲染数据";
            }
            
            bool hasWarn = false;
            var warnStr = "";
            var instancerList = m_Target.GetComponentsInChildren<InstancerPrefab>();
            if (instancerList.Length > 1)
            {
                hasWarn = true;
                warnStr = "请不要嵌套使用InstancerPrefab";
            }

            if (hasEmptyRenderData)
            {
                EditorGUILayout.HelpBox(lostStr, MessageType.Error);
            }

            if (hasWarn)
            {
                EditorGUILayout.HelpBox(warnStr, MessageType.Warning);
            }
        }

        public static string GetMeshPath(Mesh mesh)
        {
            var path = AssetDatabase.GetAssetPath(mesh);
            //path = path.Replace("Assets/res/", "");
            path = path.Substring(0, path.LastIndexOf("/", StringComparison.Ordinal));
            path = path.Substring(0, path.LastIndexOf("/", StringComparison.Ordinal));
            path = path + "/mesh_ref";

            return path;
        }

        public static void DrawCustomLabel(string text, GUIStyle style, bool center = true)
        {
            if (center)
            {
                EditorGUILayout.BeginHorizontal();
                GUILayout.FlexibleSpace();
            }

            GUILayout.Label(text, style);

            if (center)
            {
                GUILayout.FlexibleSpace();
                EditorGUILayout.EndHorizontal();
            }
        }




        [MenuItem("Assets/Instancer/刷新InstancerPrefab", false, 30)]
        public static void RefreshInstancePrefab()
        {
            var selectingObjs = Selection.GetFiltered<UnityEngine.Object>(SelectionMode.DeepAssets);
            foreach (var obj in selectingObjs)
            {
                if (obj is GameObject)
                {
                    var selectObj = obj as GameObject;
                    if (selectObj)
                    {
                        var instancers = selectObj.GetComponentsInChildren<InstancerPrefab>();
                        foreach (var instancer in instancers)
                        {
                            if (instancer)
                            {
                                instancer.Refresh();
                            }
                        }
                    }
                }
            }
            
            AssetDatabase.SaveAssets();
            AssetDatabase.Refresh();
        }

        public static float GetLodDistance(float desiredPercentage, LODGroup group)
        {
            var percentage = Mathf.Max(desiredPercentage / QualitySettings.lodBias, 0.000001f);
            var sceneView = SceneView.lastActiveSceneView;
            var sceneCamera = sceneView.camera;
            var distance = CalculateDistance(sceneCamera, percentage, group);

            return distance;
        }

        public static float CalculateDistance(Camera camera, float relativeScreenHeight, LODGroup group)
        {
            var lodUtility = typeof(LODUtility);
            var calculateDistance = lodUtility.GetMethod("CalculateDistance", BindingFlags.Instance | BindingFlags.Static | BindingFlags.NonPublic);
            var dis = (float)calculateDistance.Invoke(null, new object[]{ camera, relativeScreenHeight, group});

            return dis;
        }
        
      
        
        private static float GetScreenRelativeTransitionHeight(Camera cam, Renderer renderer)
        {
            var invSceneViewCamHeight = 1.0f / (cam.pixelHeight);
            var heightInScreen = GetHeightInScreen(cam, renderer);
            var ratioInScreen = heightInScreen * invSceneViewCamHeight;
            return ratioInScreen;
        }
        
        private static float GetScreenRelativeTransitionHeight(Camera cam, Vector3 min, Vector3 max)
        {
            var invSceneViewCamHeight = 1.0f / (cam.pixelHeight);
            var heightInScreen = HeightInScreen(cam, min, max);
            var ratioInScreen = heightInScreen * invSceneViewCamHeight;
            return ratioInScreen;
        }
    
        private static float GetHeightInScreen(Camera cam, Renderer renderer)
        {
            var min = renderer.bounds.min;
            var max = renderer.bounds.max;
            return HeightInScreen(cam, min, max);
        }

        private static float HeightInScreen(Camera cam, Vector3 min, Vector3 max)
        {
            // F = Front
            var FTL = new Vector3(min.x, max.y, min.z);
            var FTR = new Vector3(max.x, max.y, min.z);
            var FBR = new Vector3(max.x, min.y, min.z);
            var FBL = new Vector3(min.x, min.y, min.z);

            // Back
            var BTL = new Vector3(min.x, max.y, max.z);
            var BTR = new Vector3(max.x, max.y, max.z);
            var BBR = new Vector3(max.x, min.y, max.z);
            var BBL = new Vector3(min.x, min.y, max.z);

            // to screen space pos
            FTL = cam.WorldToScreenPoint(FTL);
            FTR = cam.WorldToScreenPoint(FTR);
            FBR = cam.WorldToScreenPoint(FBR);
            FBL = cam.WorldToScreenPoint(FBL);

            BTL = cam.WorldToScreenPoint(BTL);
            BTR = cam.WorldToScreenPoint(BTR);
            BBR = cam.WorldToScreenPoint(BBR);
            BBL = cam.WorldToScreenPoint(BBL);

            var maxY = FTL.y;
            maxY = Mathf.Max(FTR.y, maxY);
            maxY = Mathf.Max(FBR.y, maxY);
            maxY = Mathf.Max(FBL.y, maxY);

            maxY = Mathf.Max(BTL.y, maxY);
            maxY = Mathf.Max(BTR.y, maxY);
            maxY = Mathf.Max(BBR.y, maxY);
            maxY = Mathf.Max(BBL.y, maxY);

            var minY = FTL.y;
            minY = Mathf.Min(FTR.y, minY);
            minY = Mathf.Min(FBR.y, minY);
            minY = Mathf.Min(FBL.y, minY);

            minY = Mathf.Min(BTL.y, minY);
            minY = Mathf.Min(BTR.y, minY);
            minY = Mathf.Min(BBR.y, minY);
            minY = Mathf.Min(BBL.y, minY);

            return maxY - minY;
        }
    }
}
